from abc import ABC, abstractmethod
from typing import List, Tuple, Optional
from algorithm import Solver

class Executor(ABC):
    @abstractmethod
    def execute(self, solver: Solver, initial_values: List[float], expr: str, precision: int) -> List[Tuple[Optional[float], Optional[float], Optional[int]]]:
        pass

    @staticmethod
    @abstractmethod
    def is_available() -> bool:
        pass

class SingleThreadExecutor(Executor):
    def execute(self, solver: Solver, initial_values: List[float], expr: str, precision: int) -> List[Tuple[Optional[float], Optional[float], Optional[int]]]:
        return solver.solve(initial_values, expr, precision=precision)
    
    @staticmethod
    def is_available() -> bool:
        return True

class MultiThreadExecutor(Executor):
    def execute(self, solver: Solver, initial_values: List[float], expr: str, precision: int) -> List[Tuple[Optional[float], Optional[float], Optional[int]]]:
        from pathos.multiprocessing import ProcessingPool as Pool
        import os
        
        def worker(chunk):
            return solver.solve(chunk, expr, precision=precision)
        
        chunk_size = max(1, len(initial_values) // os.cpu_count())
        chunks = [initial_values[i:i + chunk_size] for i in range(0, len(initial_values), chunk_size)]
        
        with Pool(nodes=os.cpu_count()) as pool:
            results = pool.map(worker, chunks)
        
        return [item for sublist in results for item in sublist]
    
    @staticmethod
    def is_available() -> bool:
        try:
            import pathos
            return True
        except ImportError:
            return False